#include "RCTP.h"
#include <list>
#include <random>
#ifdef _WIN32
#include <windows.h>
#pragma comment(lib, "winmm.lib")
#else
#define INVALID_SOCKET  (int)(~0)
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#endif
#define LOG_BUF_SIZE			1024
bool RCTP::isLittleEndian;


#define SOCKET_ERROR            (-1)

#ifdef _WIN32
static void SetSystemTimerResolution(const UINT uPeriod)
{
	static UINT timer_period = 0;
	if (uPeriod != timer_period) {
		if (timer_period) {
			timeEndPeriod(timer_period);
		}

		timer_period = uPeriod;

		if (timer_period) {
			timeBeginPeriod(timer_period);
		}
	}
}

static void BeginPreciseSleep()
{
	SetSystemTimerResolution(1);
}

static void EndPreciseSleep()
{
	SetSystemTimerResolution(0);
}
#endif

RCTP::RCTP()
{
#ifdef _WIN32
	BeginPreciseSleep();
#endif
	short test = 0xff00;
	isLittleEndian = !(*(char*)&test == 0xff);
}

RCTP::~RCTP()
{
	Terminate();
}

void RCTP::Initialize()
{
	Terminate();
	id = RandomUint16();
}

void RCTP::Terminate()
{
	while (!sendMsgMap.empty())
	{
		Message* msg = sendMsgMap.begin()->second;
		sendMsgMap.erase(msg->id);
		delete msg;
	}
	while (!recvMsgMap.empty())
	{
		Message* msg = recvMsgMap.begin()->second;
		recvMsgMap.erase(msg->id);
		delete msg;
	}
}

void RCTP::Update(uint32_t timeout)
{
#if _WIN32
	Sleep(timeout);
#else
	usleep(timeout * 1000);
#endif
	HandleResend(&sendMsgMap);
	HandleResend(&recvMsgMap);
}


void RCTP::SetProtocolFrame(uint64_t frame)
{
	this->sendframe = frame;
}

uint64_t RCTP::GetProtocolFrame()
{
	return this->recvframe;
}

void RCTP::Send(uint8_t* buffer, uint32_t len, bool reliable, UserData userdata)
{
	if (reliable)
	{
		SendInternal(buffer, len, userdata);
	}
	else
	{
		SendTo(buffer, len, userdata);
	}
}

void RCTP::SendTo(uint8_t* buffer, uint32_t len, UserData userdata)
{
	if (sendimp)
		sendimp(buffer, len, userdata);
}


void RCTP::HandleBuffer(uint64_t id, uint16_t state, uint64_t frame, uint8_t* buffer, uint32_t len, UserData userdata)
{
	if (state == PackageState::PSH)
	{
		auto result = recvMsgMap.find(id);
		if (result == recvMsgMap.end())
		{
			recvframe = frame;
			OnRecvEvent(buffer, len, true, userdata);
			SendInternalACK(id, ACK, &recvMsgMap, userdata);
		}
	}
	else if (state == PackageState::ACK)
	{
		auto result = sendMsgMap.find(id);
		if (result != sendMsgMap.end())
		{
			result->second->OnSendEvent(this, SS_Success);
			Message* msg = sendMsgMap[id];
			sendMsgMap.erase(id);
			delete msg;
		}
		uint8_t* data = AssemblyBuffer(id, PackageState::BKACK, this->sendframe);
		SendTo(data, HEAD_SIZE, userdata);
		delete[] data;
	}
	else if (state == PackageState::BKACK)
	{
		auto result = recvMsgMap.find(id);
		if (result != recvMsgMap.end())
		{
			Message* msg = recvMsgMap[id];
			recvMsgMap.erase(id);
			delete msg;
			SendInternalACK(id, RMACK, &recvMsgMap, userdata);	
		}
	}
	else if (state == PackageState::RMACK)
	{
		uint8_t* data = AssemblyBuffer(id, PackageState::ENDACK, this->sendframe);
		SendTo(data, HEAD_SIZE, userdata);
		delete[] data;
	}
	else if (state == PackageState::ENDACK)
	{
		auto result = recvMsgMap.find(id);
		if (result != recvMsgMap.end())
		{
			Message* msg = recvMsgMap[id];
			recvMsgMap.erase(id);
			delete msg;
		}
	}
}

void RCTP::HandleResend(std::map<uint64_t, Message*>* map)
{
	uint32_t ts = GetTimestamp();
	std::list<uint64_t> removes;

	for (std::map<uint64_t, Message*>::iterator iter = map->begin(); iter != map->end(); iter++)
	{
		Message* msg = iter->second;
		if (ts - msg->sendTs > MAX_RESEND_TIMEOUT)
		{
			if (msg->resendCount >= MAX_RESEND_GROUP_COUNT)
			{
				msg->OnSendEvent(this, SS_MaxTimeout);
				if(nextClear)
					break;
				removes.push_back(msg->id);
			}
			else
			{
				if (ResetTime)
				{
					msg->waitTimeout = DEFAULT_TIMEOUT;
					msg->sendTs = GetTimestamp();
					SendTo((uint8_t*)msg->data, msg->len, msg->userdata);
					msg->resendCount++;
				}
				else if(ts - msg->sendTs > msg->waitTimeout)
				{
					msg->waitTimeout = ts - msg->sendTs + MAX_RESEND_TIMEOUT;
					msg->OnSendEvent(this, SS_Timeout);
					if (nextClear)
						break;
					SendTo((uint8_t*)msg->data, msg->len, msg->userdata);
					msg->resendCount++;
				}
			}
		}
		else if (ts - msg->sendTs > msg->waitTimeout)
		{
			msg->waitTimeout = (uint32_t)(msg->waitTimeout * Multiple);
			SendTo((uint8_t*)msg->data, msg->len, msg->userdata);
		}
	}
	while (!removes.empty())
	{
		Message* msg = (*map)[removes.front()];
		auto result = (*map).find(removes.front());
		map->erase(removes.front());
		removes.pop_front();
		delete msg;
	}
	if (nextClear)
	{
		nextClear = false;
		Terminate();
	}
}

void RCTP::InputOriginRecv(uint8_t* buffer, uint32_t len, UserData userdata)
{
	if (VerifyBuffer((uint8_t*)buffer, len))
	{
		uint64_t id = *(uint64_t*)buffer;
		uint16_t state = *(uint16_t*)(buffer + 8);
		uint64_t frame = *(uint64_t*)(buffer + 12);
		HandleBuffer(id, state, frame,(uint8_t*)buffer + HEAD_SIZE, len - HEAD_SIZE, userdata);
	}
	else
	{
		OnRecvEvent((uint8_t*)buffer, len, false, userdata);
	}
}

void RCTP::OnRecvEvent(uint8_t* buffer, uint32_t len, bool reliable, UserData userdata)
{
	if (recvcb)
		recvcb(buffer, len, reliable, userdata);
}

void RCTP::SendInternal(uint8_t* buffer, uint32_t len, UserData userdata)
{
	if (len > BUFF_SIZE)
	{
		ErrorLog("buffer length (%d) > MAX_BUFF_SIZE %d", len, BUFF_SIZE);
		throw;
	}

	Message* msg = new Message();
	msg->id = AssemblyID(GetTimestamp(), RandomUint16(), id++);
	msg->waitTimeout = DEFAULT_TIMEOUT;
	msg->sendTs = GetTimestamp();
	msg->userdata = userdata;
	msg->state = PackageState::PSH;
	msg->data = (pointer_t)AssemblyBuffer(msg->id, msg->state, sendframe, buffer, len);
	msg->len = HEAD_SIZE + len;
	msg->resendCount = 0;
	std::pair<std::map<uint64_t, Message*>::iterator, bool> insertPair;
	insertPair = sendMsgMap.insert(std::map<uint64_t, Message*>::value_type(msg->id, msg));
	if (!insertPair.second)
	{
		ErrorLog("Insert to sendMsgMap failed");
		throw;
	}
	msg->OnSendEvent(this, SS_BeforeSend);
	SendTo((uint8_t*)msg->data, msg->len, userdata);
}

void RCTP::SendInternalACK(uint64_t id, PackageState state, std::map<uint64_t, Message*>* map, UserData userdata)
{
	Message* msg = new Message();
	msg->id = id;
	msg->waitTimeout = DEFAULT_TIMEOUT;
	msg->sendTs = GetTimestamp();
	msg->userdata = userdata;
	msg->state = state;
	msg->data = (pointer_t)AssemblyBuffer(id, msg->state, this->sendframe);
	msg->len = HEAD_SIZE;
	msg->resendCount = 0;
	std::pair<std::map<uint64_t, Message*>::iterator, bool> insertPair;
	insertPair = map->insert(std::map<uint64_t, Message*>::value_type(msg->id, msg));
	if (!insertPair.second)
	{
		ErrorLog("Insert to sendMsgMap failed");
		throw;
	}
	msg->OnSendEvent(this, SS_BeforeSend);
	SendTo((uint8_t*)msg->data, msg->len, userdata);
}

void RCTP::ErrorLog(const char* _Format, ...)
{
	va_list ap;
	char buf[LOG_BUF_SIZE] = { 0 };
	va_start(ap, _Format);
	vsnprintf(buf, LOG_BUF_SIZE, _Format, ap);
	va_end(ap);

	if (onErrorLog)
		onErrorLog(buf);
	else
		printf("%s\n", buf);
}

static int64_t gettime_internal()
{
#if _WIN32
	FILETIME ft;
	int64_t t;
	GetSystemTimeAsFileTime(&ft);
	t = (int64_t)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
	return t / 10 - 11644473600000000;
#else
	struct timeval tv;
	gettimeofday(&tv, NULL);
	return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
#endif
}


uint16_t RCTP::RandomUint16()
{
	static int rndSeed = 0;
	rndSeed++;
	srand(GetTimestamp() + rndSeed);
	return rand();
}


void RCTP::Clear()
{
	nextClear = true;
}

uint32_t RCTP::GetTimestamp()
{
	return (uint32_t)((gettime_internal() / 1000) & 0xfffffffful);
}

float RCTP::RandomSingle()
{
	std::random_device rd;
	std::default_random_engine generator(rd());
	std::uniform_real_distribution<float> unif(0.0, 1.0);
	return unif(generator);
}

void RCTP::SetOption(int option, uint32_t value)
{
	switch (option)
	{
	case OPTION_MAX_RESENT_GROUP_COUNT:
		MAX_RESEND_GROUP_COUNT = value;
		break;
	case OPTION_DEFAULT_TIMEOUT:
		if (value < 50)
			value = 50;
		DEFAULT_TIMEOUT = value;
		break;
	case OPTION_MAX_RESEND_TIMEOUT:
		if (value < 2000)
			value = 2000;
		MAX_RESEND_TIMEOUT = value;
		break;
	case OPTION_RESET_TIME:
		ResetTime = value != 0;
		break;
	}
}

void RCTP::SetOption(int option, float value)
{
	switch (option)
	{
	case OPTION_MULTIPLE:
		if (value < 1.1f)
			value = 1.1f;
		Multiple = value;
		break;
	}
}

uint64_t RCTP::AssemblyID(uint32_t ts, uint16_t r, uint16_t lid)
{
	uint64_t value;
	uint8_t* data = (uint8_t*)&value;
	memcpy(data, &ts, 4);
	memcpy(data + 4, &r, 2);
	memcpy(data + 6, &lid, 2);
	return value;
}

uint8_t* RCTP::AssemblyBuffer(uint64_t id, uint16_t state, uint64_t frame, uint8_t* data, uint32_t len)
{
	uint8_t* buffer = new uint8_t[HEAD_SIZE + len];
	uint8_t* assBuf = AssemblyBuffer(id, state, frame);
	memcpy(buffer, assBuf, HEAD_SIZE);
	delete assBuf;
	memcpy(buffer + HEAD_SIZE, data, len);
	return buffer;
}

uint8_t* RCTP::AssemblyBuffer(uint64_t id, uint16_t state, uint64_t frame)
{
	uint8_t* buffer = new uint8_t[HEAD_SIZE];
	memcpy(buffer, &id, 8);
	memcpy(buffer + 8, &state, 2);
	uint16_t sum = 0;
	for (int i = 0; i < 10; i++)
		sum += buffer[i];
	memcpy(buffer + 10, &sum, 2);
	memcpy(buffer + 12, &frame, sizeof(uint64_t));
	return buffer;
}

bool RCTP::VerifyBuffer(uint8_t* buffer, uint32_t length)
{
	if (length < HEAD_SIZE)
		return false;
	int sum = 0;
	for (int i = 0; i < 10; i++)
		sum += buffer[i];
	if (sum != *((uint16_t*)(buffer + 10)))
		return false;
	return true;
}

void Message::OnSendEvent(RCTP* rctp, SendState state)
{
	if (rctp->sendcb)
		rctp->sendcb(this, state);
}

Message::~Message()
{
	delete[] ((uint8_t*)data);
}
